# vue和ext组件互相兼容使用方案

# 背景

目前控制器项目是vue做底座,嵌入ext页面的方案。

由于许多组件都是基于ext开发的,比如端口面板、端口选择、交换机选择器等等复杂组件,重新开发成本很大,于是想出一套两框架间组件能互相调用的方案

# vue中使用ext组件

目前新的表格页面中有个新增的表单,其中有端口组、选择交换机、短信通知等组件,是ext早就有的组件,如果重新开发成本巨大,于是决定这个新增弹窗使用ext去写。

那么vue中如何调用ext组件呢?

# 编写mgr

在ext中,页面通常由page、mgr构成,其中弹窗之类的都是写在mgr中,所以我们需要编写一个mgr去调用各弹窗组件。

结构通常如下:

export const Mgr = Ext.extend(SFX.mgr.GridMgr, {
    initMgr: function () {},
    actions: {},
    createFormWindow: function () {},
});

# 调用

在actions中写好要使用的action后,编写一个调用函数:

import { Mgr } from "./Mgr";

export const openStrategyFrom = ({
  id,
  loadData,
}) => {
  const mgr = new Mgr({
    loadData,
  });
  mgr.executeAction(
    {
      id,
    },
    isNumber(id) ? "modify" : "add"
  );
};

实例化刚才写的mgr后,通过executeAction触发指定的action,弹窗就能出现了。

注:前提是项目底层已加载ext源码,且处理好了弹窗层级关系。

vue页面通常只需传入一个成功的回调,在ext弹窗内部执行完业务后调用回调即可实现数据同步。

# ext中使用vue组件

# 无框架间交互

vue组件不需受ext控制时,处理起来就很简单

写一个容器,在其渲染完后,将vue组件通过createApp+mount挂载在ext的容器上即可。

{
    xtype: 'box',
    listeners: {
        'afterrender': function () {
            const id = this.id;
            this.App = createApp(VueComponent, {});
            this.App.mount('#' + id, true);
        },
        'destroy': function () {
            this.JumpButtonApp?.unmount();
            this.JumpButtonApp = null;
        }
    }
}

如果是纯展示的(只需要其渲染出html),通过hrender也是可以的。

# 框架间需要交互

这里以ext表单嵌入vue组件举例。

# ext容器

由于需要两框架间进行交互,我们需要先弄个ext容器,这里由于是表单中,便继承Ext.form.TriggerField,这是为了触发ext的校验等操作,换普通box组件是没有这效果的。

同理,在渲染后,通过createApp+mount渲染vue组件,需要注意的是,要将ext原组件给覆盖渲染。

同时需配上:

setJsonValue:可以自己弄个字段去记录值,但是设置的时候不要忘了给当前ext设置一个值,不然校验会不通过

getJsonValue:返回记录的值即可

validate:手动调用vue组件去触发校验,不要忘了触发ext的原组件(弹错误路径)

onDestroy:销毁时需同步销毁

export const TerminalTypeCombo = Ext.extend(Ext.form.TriggerField, {
  onRender: function (..._args) {
    TerminalTypeCombo.superclass.onRender.apply(this, _args);
    this.createTerminalTypeSelect();
  },
  createTerminalTypeSelect: function () {
    me.terminalTypeSelectApp = createApp(TerminalTypeSelect, {
      disabled,
      value: me.jsonValue,
      isFullPath: me.isFullPath,
      fetchParams: me.fetchParams,
      networkType: me.networkType,
      allowBlank: me.allowBlank,
      ...me.props,
      onSelect: (me.props?.onSelect || me.onSelect)?.bind(me),
    });

    // 强行覆盖在父元素内
    me.terminalTypeSelect = me.terminalTypeSelectApp.mount(
      me.el.dom.parentElement,
      true
    );
  },
  setJsonValue: function (v) {
      
  },
  getJsonValue: function () {
      
  },
  validate: function () {
    this.terminalTypeSelect.checkValid();
    return TerminalTypeCombo.superclass.validate.call(this);
  },
  onDestroy: function (..._args) {
    TerminalTypeCombo.superclass.onDestroy.apply(this, _args);
    this.terminalTypeSelectApp?.unmount();
  },
});

# vue组件

vue组件相信大家都写的很熟练,ext所需要调用的方法都通过defineExpose暴露出去即可。

处理起来比较麻烦的是校验状态的显示

用表单那一套处理起来很复杂,这里通过简单的办法实现。

<template>
  <IxTooltip
    :title="message"
    :disabled="!message"
    placement="topStart"
    class="ix-form-item-message-tooltip">
    <div class="terminal-type-select-wrap">
      <IxsTreeSelect
        :status="status">
      </IxsTreeSelect>
    </div>
  </IxTooltip>
</template>

通过IxTooltip包一下,记得添加class="ix-form-item-message-tooltip",红色的状态提示,与项目保持一致。

然后手动根据数据判断是否校验通过,如果没通过则将错误信息传给tooltip,同时给自己的组件设置正确的校验状态,即可实现校验的效果。

其它类型的组件处理起来也是同理,做一个中间层,解决数据、校验、销毁等的联通即可实现。

# 总结

实现框架间互相调用,可避免组件的重新开发,快速实现需求。